﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using AssetLoad;

namespace Room
{
    public class RoomModelPool : MonoBehaviour
    {
        private Dictionary<string, ModelPool> cachePool = new Dictionary<string, ModelPool>();

        #region Singleton
        private static RoomModelPool _instance;
        public static RoomModelPool Instance
        {
            get
            {
                return _instance;
            }
        }
        void Awake()
        {
            _instance = this;
        }
        #endregion

        #region Recycle
        private const int DETECT_SPACE = 10;
        private int detectCount = 0;
        void RecycleTick()
        {
            if (++detectCount > DETECT_SPACE)
            {
                detectCount = 0;
                List<string> recycles = new List<string>();
                var enumer = cachePool.GetEnumerator();
                while (enumer.MoveNext())
                {
                    if (enumer.Current.Value.ShouldClear())
                    {
                        enumer.Current.Value.ClearPool();
                        recycles.Add(enumer.Current.Key);
                    }
                }
                for (int i = 0; i < recycles.Count; ++i)
                {
                    cachePool.Remove(recycles[i]);
                }
            }
        }
        #endregion

        bool _isLimitLoad;
        bool IsLimitLoad
        {
            get
            {
                return _isLimitLoad;
            }
            set
            {
                if (_isLimitLoad != value)
                {
                    instCount = 0;
                    loadingPool.Clear();
                }
                _isLimitLoad = value;
            }
        }

        public void SwitchRoom(SceneType t)
        {
            if (t == SceneType.TOWN || t == SceneType.FIELD)
            {
                IsLimitLoad = true;
            }
            else
            {
                IsLimitLoad = false;
            }
        }

        const int MAX_LOAD_COUNT = 1;
        int loadingCount
        {
            get
            {
                return loadingPool.Count;
            }
        }
        List<ModelPool> loadingPool = new List<ModelPool>();

        const int MAX_INST_COUNT = 2;
        int instCount = 0;

        public GameObject GetModelFromCache(string modelName)
        {
            if (IsLimitLoad)
            {
                if (!cachePool.ContainsKey(modelName))
                {
                    if (loadingCount < MAX_LOAD_COUNT)
                    {
                        cachePool[modelName] = new ModelPool(modelName, this);
                        loadingPool.Add(cachePool[modelName]);
                    }
                }
                if (cachePool.ContainsKey(modelName) && cachePool[modelName].initComplete)
                {
                    if (instCount < MAX_INST_COUNT)
                    {
                        instCount++;
                        return cachePool[modelName].GetModel();
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return null;
                }
            }
            else
            {
                if (!cachePool.ContainsKey(modelName))
                {
                    cachePool[modelName] = new ModelPool(modelName, this);
                }
                if (!cachePool[modelName].initComplete)
                {
                    return null;
                }
                return cachePool[modelName].GetModel();
            }
        }

        void Update()
        {
            RecycleTick();

            if (IsLimitLoad)
            {
                if (loadingPool.Count > 0)
                {
                    List<ModelPool> removes = new List<ModelPool>();
                    foreach (var pool in loadingPool)
                    {
                        if (pool.initComplete || pool.isError)
                        {
                            removes.Add(pool);
                        }
                    }
                    foreach (var re in removes)
                    {
                        loadingPool.Remove(re);
                    }
                }

                instCount = 0;
            }
        }

        public GameObject GetModelFromCache0(string modelName) {
            if (!cachePool.ContainsKey(modelName))
            {
                cachePool[modelName] = new ModelPool(modelName, this);
            }
            if (!cachePool[modelName].initComplete) {
                return null;
            }
            return cachePool[modelName].GetModel();
        }

        public void PutModelToCache(string modelName, GameObject model)
        {
            List<ModelPool> removes = new List<ModelPool>();
            foreach (var pool in loadingPool)
            {
                if (pool.modelName == modelName)
                {
                    removes.Add(pool);
                }
            }
            foreach (var pool in removes)
            {
                loadingPool.Remove(pool);
            }
            if(model==null)
            {
                return;
            }
            if (!cachePool.ContainsKey(model.name))
            {
                Debug.LogError("pool error");
                GameObject.Destroy(model);
                return;
            }
            cachePool[model.name].RecycleModel(model);
        }

        public void FlushCache()
        {
            List<string> recycles = new List<string>();
            var enumer = cachePool.GetEnumerator();
            while (enumer.MoveNext())
            {
                if (enumer.Current.Value.ShouldForceClear())
                {
                    enumer.Current.Value.ClearPool();
                    recycles.Add(enumer.Current.Key);
                }
            }
            for (int i = 0; i < recycles.Count; ++i)
            {
                cachePool.Remove(recycles[i]);
            }
        }

        public void Clear()
        {
            foreach (var modelPair in cachePool)
            {
                modelPair.Value.ClearPool();
            }
            cachePool.Clear();
        }

        public class ModelPool
        {
            public float lastInactiveTime = 0;
            public int usedNumber = 0;
            public int cacheNumber = 0;
            public string modelName;
            const string prefix = "model/";
            const float RECYCLE_TIME = 10;

            public bool initComplete = false;
            public bool isError = false;
            public RoomModelPool poolCtrl;
            private bool beRycycled = false;


            public ModelPool(string name, RoomModelPool _ctrl )
            {
                this.modelName = name;
                this.poolCtrl = _ctrl;
                lastInactiveTime = 0;
                usedNumber = 0;
                cacheNumber = 0;
                string path = prefix + modelName;
                initComplete = false;

#if !FIX_RESOURCE
                //AssetBundleManager.Instance.LoadAssetBundle(path);
                poolCtrl.StartCoroutine(AssetBundleManager.Instance.LoadAndInstAssetCoroutine(path, modelName, LoadOver));
#else
                ResourceManager.Instance.LoadResource(path);
#endif
                //poolCtrl.StartCoroutine(LoadPool(path, name));
            }

            private void LoadOver(bool success)
            {
                if(success)
                {
                    GameObjectPoolManager.Instance.Regist(prefix + modelName, modelName);
                    // 更新lastInactiveTime，避免刚加载完，直接ShouldClear
                    lastInactiveTime = Time.unscaledTime;
                    initComplete = true;
                }
                else
                {
                    isError = true;
                }
            }

            IEnumerator LoadPool(string abName, string assetName) {
#if !FIX_RESOURCE
                while (!AssetBundleManager.Instance.IsLoadedAssetBundle(abName)) {
                    if(beRycycled)
                    {
                        yield break;
                    }
                    yield return null;
                }
                GameObjectPoolManager.Instance.Regist(abName, assetName);
#else
                GameObjectPoolManager.Instance.Regist(abName);
                yield return 0;
#endif
                initComplete = true;
                // 更新lastInactiveTime，避免刚加载完，直接ShouldClear
                lastInactiveTime = Time.unscaledTime;
            }


            public GameObject GetModel()
            {
#if !FIX_RESOURCE
                string abName = prefix + modelName;
                string assetName = modelName;
                GameObject obj = GameObjectPoolManager.Instance.Spawn(abName, assetName);
#else
                GameObject obj = GameObjectPoolManager.Instance.Spawn(prefix + modelName);
#endif
                obj.name = modelName;
                ++usedNumber;
                return obj;
            }

            public void RecycleModel(GameObject obj)
            {
                if (cacheNumber > usedNumber / 2)
                {
                    GameObject.Destroy(obj);
                }
                else
                {
                    GameObjectPoolManager.Instance.Unspawn(obj);
                    ++cacheNumber;
                }
                if (--usedNumber <= 0)
                {
                    lastInactiveTime = Time.unscaledTime;
                }
            }

            public bool ShouldForceClear()
            {
                return usedNumber <= 0;
            }

            public bool ShouldClear()
            {
                return initComplete && usedNumber <= 0 && Time.unscaledTime - lastInactiveTime > RECYCLE_TIME;
            }

            public void ClearPool()
            {
                string path = prefix + modelName;
#if !FIX_RESOURCE
                GameObjectPoolManager.Instance.UnRegist(path, modelName);
                AssetBundleManager.Instance.UnloadAssetBundle(path);
#else
                GameObjectPoolManager.Instance.UnRegist(path);
                ResourceManager.Instance.UnloadResource(path);
#endif
                cacheNumber = 0;
                beRycycled = true;
                initComplete = false;
            }
        }
    }


}
